package org.example.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.example.enums.BizCodeEnum;
import org.example.enums.CouponCategoryEnum;
import org.example.enums.CouponPublishEnum;
import org.example.enums.CouponStateEnum;
import org.example.exception.BizException;
import org.example.interceptor.LoginInterceptor;
import org.example.mapper.CouponMapper;
import org.example.mapper.CouponRecordMapper;
import org.example.model.CouponDO;
import org.example.model.CouponRecordDO;
import org.example.model.LoginUser;
import org.example.request.NewUserCouponRequest;
import org.example.service.CouponService;
import org.example.util.CommonUtil;
import org.example.util.JsonData;
import org.example.vo.CouponVO;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.time.Duration;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Slf4j
public class CouponServiceImpl implements CouponService {

    @Autowired
    private CouponMapper couponMapper;

    @Autowired
    private CouponRecordMapper couponRecordMapper;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    /**
     * 分页查询优惠券
     */
    @Cacheable(value = {"coupon"}, key = "#root.methodName + #page + '_' + #size")
    @Override
    public Map<String, Object> pageCouponActivity(int page, int size) {
        Page<CouponDO> pageInfo = new Page<>(page, size);
        IPage<CouponDO> couponDOIPage = couponMapper.selectPage(pageInfo, new QueryWrapper<CouponDO>()
                .eq("publish", CouponPublishEnum.PUBLISH.name())
                .eq("category", CouponCategoryEnum.PROMOTION.name())
                .orderByDesc("create_time"));
        //因为不能直接返回DO，所以将分页对象转换为Map
        Map<String, Object> pageMap = new HashMap<>();
        //总记录数
        pageMap.put("total_record", couponDOIPage.getTotal());
        //总页数
        pageMap.put("total_page", couponDOIPage.getPages());
        //具体数据
        List<CouponVO> couponVOList = couponDOIPage.getRecords()
                .stream()
                .map(obj -> toCouponVO(obj))
                .collect(Collectors.toList());
        pageMap.put("current_data", couponVOList);
        return pageMap;
    }

    /**
     * 领取促销优惠券（promotion）
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    @Override
    public JsonData addCoupon(Long couponId, CouponCategoryEnum category) {

        //1、获取登录用户的信息
        LoginUser user = LoginInterceptor.threadLocal.get();
        //String lockKey = "lock:coupon:" + couponId + ":" + user.getId();
        //2、用户的id
        Long userId = user.getId();
        //RLock rLock = redissonClient.getLock(lockKey);
        //rLock.lock();

        // 加锁成功执行业务逻辑
        //try {
        //3、查询库存中的优惠券
        CouponDO couponDO = couponMapper.selectOne(new QueryWrapper<CouponDO>()
                .eq("id", couponId)
                .eq("category", category));
        //4、优惠券是否可以领取
        this.checkCoupon(couponDO, userId);
        //5、以上不抛异常（可以领取）则构建“领券记录”
        CouponRecordDO recordDO = new CouponRecordDO();
        BeanUtils.copyProperties(couponDO, recordDO);

        recordDO.setId(null);//不能跟couponDO一样，要自动生成id
        recordDO.setCouponId(couponId);
        recordDO.setCreateTime(new Date());
        recordDO.setUserId(userId);
        recordDO.setUserName(user.getName());
        recordDO.setUseState(CouponStateEnum.NEW.name());

        //库存中减1
        int rows = couponMapper.reduceStock(couponId);
        if (rows == 1) {
            //库存减1后，保存记录
            couponRecordMapper.insert(recordDO);
        } else {
            log.warn("优惠券领取失败");
            throw new BizException(BizCodeEnum.COUPON_NO_STOCK);
        }
        //} finally {
        //    log.info("解锁成功");
        //   rLock.unlock();
        // }

        return JsonData.buildSuccess();
    }

    /**
     * 新人领取优惠券
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    @Override
    public JsonData initNewUserCoupon(NewUserCouponRequest newUserCouponRequest) {
        //新人注册后是没有token的，需要手动设置user_id
        LoginUser loginUser = LoginUser.builder().build();
        loginUser.setId(newUserCouponRequest.getUserId());
        loginUser.setName(newUserCouponRequest.getName());
        //设置到threadLocal，方便获取
        LoginInterceptor.threadLocal.set(loginUser);
        //查询出NEW类型的优惠券
        List<CouponDO> couponDOList = couponMapper.selectList(new QueryWrapper<CouponDO>()
                .eq("category", CouponCategoryEnum.NEW_USER.name()));
        for (CouponDO couponDO : couponDOList) {
            //幂等操作，需要加锁
            this.addCoupon(couponDO.getId(), CouponCategoryEnum.NEW_USER);
        }
        return JsonData.buildSuccess();
    }

    /**
     * 检查优惠券是否能使用
     */
    private void checkCoupon(CouponDO couponDO, Long userId) {
        //1、优惠券是否存在
        if (couponDO == null) {
            throw new BizException(BizCodeEnum.COUPON_NO_EXITS);
        }
        //2、库存是否足够
        if (couponDO.getStock() <= 0) {
            throw new BizException(BizCodeEnum.COUPON_NO_STOCK);
        }
        //3、是否为发布状态
        if (!couponDO.getPublish().equals(CouponPublishEnum.PUBLISH.name())) {
            throw new BizException(BizCodeEnum.COUPON_GET_FAIL);
        }
        //4、是否在限定时间内领取
        long time = CommonUtil.getCurrentTimestamp();
        long startTime = couponDO.getStartTime().getTime();
        long endTime = couponDO.getEndTime().getTime();
        if (time < startTime || time > endTime) {
            throw new BizException(BizCodeEnum.COUPON_OUT_OF_TIME);
        }
        //5、用户是否超过限制
        int count = couponRecordMapper.selectCount(new QueryWrapper<CouponRecordDO>()
                .eq("user_id", userId)
                .eq("coupon_id", couponDO.getId()));
        if (count >= couponDO.getUserLimit()) {
            throw new BizException(BizCodeEnum.COUPON_OUT_OF_LIMIT);
        }

    }

    /**
     * 将CouponDO转为CouponVO
     */
    private CouponVO toCouponVO(CouponDO couponDO) {
        CouponVO couponVO = new CouponVO();
        BeanUtils.copyProperties(couponDO, couponVO);
        return couponVO;
    }

}
